<template>

<div
  ref="container"
  :class="{
    dark: true,
    'full-height': !isAddingEntity || isLoading,
    'playlist-player': true
  }"
>
  <div
    ref="header"
    class="playlist-header flexrow"
    v-if="!tempMode"
  >
    <div
      class="flexrow-item for-client"
      v-if="playlist && playlist.for_client"
    >
      {{ $t('playlists.client_playlist') }}
    </div>
    <span class="flexrow-item playlist-name">
      {{ playlist.name }}
    </span>
    <div class="flexrow-item playlist-room">
      <button-simple
        @click="leaveRoom"
        class="playlist-button topbar-button"
        :text="$t('playlists.leave_room')"
        v-if="joinedRoom"
      />
      <button-simple
        @click="joinRoom"
        class="playlist-button topbar-button"
        :text="$t('playlists.join_room')"
        v-else
      />
      <people-avatar
        class="person-avatar"
        :key="personEmailMap.get(personEmail).id"
        :person="personEmailMap.get(personEmail)"
        :size="30"
        :font-size="15"
        v-for="personEmail in room.people"
        v-if="personEmailMap.get(personEmail)"
      />
    </div>
    <button-simple
      @click="$emit('show-add-entities')"
      class="playlist-button topbar-button flexrow-item"
      icon="plus"
      :text="addEntitiesText"
      v-if="isCurrentUserManager && !isAddingEntity"
    />
    <button-simple
      @click="$emit('edit-clicked')"
      class="edit-button playlist-button flexrow-item"
      :title="$t('playlists.actions.edit')"
      icon="edit"
      v-if="isCurrentUserManager"
    />
    <button-simple
      @click="showDeleteModal"
      class="delete-button playlist-button flexrow-item"
      :title="$t('playlists.actions.delete')"
      icon="delete"
      v-if="isCurrentUserManager"
    />
  </div>

  <div
    class="flexrow filler"
    v-show="!isAddingEntity || isLoading"
  >
    <div
      :class="{
         filler: true,
         flexrow: true,
         'video-container': true,
         'flexrow-reverse': !isComparisonOverlay
      }"
      ref="video-container"
    >

      <raw-video-player
        ref="raw-player-comparison"
        class="raw-player"
        :style="{
          position: isComparisonOverlay ? 'absolute': 'static'
        }"
        :entities="entityListToCompare"
        :full-screen="fullScreen"
        :is-hd="isHd"
        :is-repeating="isRepeating"
        :muted="true"
        name="comparison"
        v-show="isComparing && isCurrentPreviewMovie &&
                isMovieComparison && !isLoading"
      />

      <div
        class="picture-preview-comparison-wrapper"
        :style="{
          position: isComparisonOverlay ? 'absolute': 'static',
          left: 0,
          right: 0
        }"
        v-show="
          isComparing &&
          !isLoading &&
          !isCurrentPreviewFile &&
          (
            (isCurrentPreviewMovie && !isMovieComparison) ||
            !isCurrentPreviewMovie
          )
        "
      >
         <img
           ref="picture-player-comparison"
           class="picture-preview"
           :src="currentComparisonPreviewPath"
           v-show="isComparing && isPictureComparison"
         />
         <span
           class="picture-preview"
           v-show="isComparing && !isPictureComparison"
         >
           It's not a picture preview
         </span>
      </div>

      <raw-video-player
        ref="raw-player"
        class="raw-player"
        :style="{
          position: isComparisonOverlay ? 'absolute': 'static',
          opacity: overlayOpacity
        }"
        :entities="entityList"
        :full-screen="fullScreen"
        :is-hd="isHd"
        :is-repeating="isRepeating"
        :current-preview-index="currentPreviewIndex"
        :muted="isMuted"
        @entity-change="onPlayerEntityChange"
        @max-duration-update="onMaxDurationUpdate"
        @metadata-loaded="onMetadataLoaded"
        @play-next="onPlayNext"
        @repeat="onVideoRepeated"
        @frame-update="onFrameUpdate"
        v-show="isCurrentPreviewMovie && !isLoading"
      />

      <object-viewer
        ref="object-player"
        class="object-player"
        :preview-url="currentPreviewDlPath"
        :style="{
          position: isComparisonOverlay ? 'absolute': 'static',
          opacity: overlayOpacity
        }"
        :full-screen="fullScreen"
        v-if="isCurrentPreviewModel && !isLoading"
      />

      <sound-viewer
        ref="sound-player"
        class="sound-player"
        :preview-url="currentPreviewDlPath"
        :full-screen="fullScreen"
        @play-ended="pause"
        v-if="isCurrentPreviewSound && !isLoading"
      />

      <p
        :style="{width: '100%'}"
        class="preview-standard-file has-text-centered"
        v-show="isCurrentPreviewFile && !isLoading"
      >
        <a
          class="button"
          ref="preview-file"
          :href="currentPreviewDlPath"
          v-if="extension && extension.length > 0"
        >
          <download-icon class="icon" />
          <span class="text">
            {{ $t('tasks.download_pdf_file', {extension: extension}) }}
          </span>
        </a>
      </p>

      <div
        class="picture-preview-wrapper flexrow"
        ref="picture-player-wrapper"
        :style="{
          position: isComparisonOverlay ? 'absolute': 'static',
          opacity: overlayOpacity,
          left: 0,
          right: 0
        }"
        v-show="isCurrentPreviewPicture && !isLoading"
      >
         <img
           ref="picture-player"
           id="picture-player"
           class="picture-preview"
           :src="isCurrentPreviewPicture ? currentPreviewPath : null"
           v-show="isCurrentPreviewPicture"
         />
      </div>

      <div class="loading-wrapper" v-if="isLoading">
        <spinner />
      </div>

      <div
        class="canvas-wrapper"
        ref="canvas-wrapper"
        oncontextmenu="return false;"
        v-show="!isCurrentPreviewFile"
      >
        <canvas
          id="playlist-annotation-canvas"
          ref="annotation-canvas"
          class="canvas"
        >
        </canvas>
      </div>
    </div>

    <task-info
      ref="task-info"
      :class="{
        'flexrow-item': true,
        'task-info-column': true,
        'hidden': isCommentsHidden
      }"
      :task="task"
      :is-preview="false"
      :silent="isCommentsHidden"
      :current-time-raw="currentTimeRaw - frameDuration"
      :current-parent-preview="currentPreview"
      @time-code-clicked="onTimeCodeClicked"
    />
  </div>

  <video-progress
    ref="video-progress"
    class="video-progress pull-bottom"
    :annotations="annotations"
    :frame-duration="frameDuration"
    :nb-frames="nbFrames"
    @start-scrub="$refs['button-bar'].classList.add('unselectable')"
    @end-scrub="$refs['button-bar'].classList.remove('unselectable')"
    @progress-changed="onProgressChanged"
    v-show="isCurrentPreviewMovie && playlist.id && !isAddingEntity"
  />

  <div
    class="playlist-footer flexrow"
    ref="button-bar"
    v-if="playlist.id && !isAddingEntity"
  >
    <div
      class="flexrow flexrow-item mr0"
      v-if="
        isCurrentPreviewMovie ||
        isCurrentPreviewPicture ||
        isCurrentPreviewSound"
    >
      <button-simple
        class="button playlist-button flexrow-item"
        @click="playClicked"
        :title="$t('playlists.actions.play')"
        icon="play"
        v-if="!isPlaying"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="pauseClicked"
        :title="$t('playlists.actions.pause')"
        icon="pause"
        v-else
      />
    </div>
    <div class="separator"></div>

    <button-simple
      class="button playlist-button flexrow-item"
      @click="onPlayPreviousEntityClicked"
      :title="$t('playlists.actions.previous_shot')"
      icon="back"
    />
    <button-simple
      class="playlist-button flexrow-item"
      @click="onPlayNextEntityClicked"
      :title="$t('playlists.actions.next_shot')"
      icon="forward"
    />
    <div class="separator"></div>
    <span
      class="flexrow-item time-indicator"
      :title="$t('playlists.actions.entity_index')"
    >
      {{ entityList.length > 0 ? playingEntityIndex + 1 : 0 }}
    </span>
    <span class="flexrow-item time-indicator">
    /
    </span>
    <span
      class="flexrow-item time-indicator mr1"
      :title="$t('playlists.actions.entities_number')"
    >
      {{ entityList.length }}
    </span>

    <div class="separator ml1"></div>
    <template v-if="isCurrentPreviewPicture">
      {{ framesSeenOfPicture }} /
      <input
        type="number"
        min="0"
        class="frame-per-image-input"
        :title="$t('playlists.actions.frames_per_picture')"
        v-model="framesPerImage[playingEntityIndex]"
      >
    </template>
    <div class="separator" v-if="isCurrentPreviewPicture"></div>

    <div
      class="flexrow flexrow-item"
      v-if="currentEntityPreviewLength > 1"
    >
      <button-simple
        class="button playlist-button flexrow-item"
        icon="left"
        :title="$t('playlists.actions.files_previous')"
        :disabled="isPlaying"
        @click="onPreviousPreviewClicked"
      />
      <span
        class="ml05 mr05"
        :title="$t('playlists.actions.files_position')"
      >
        {{ currentPreviewIndex + 1 }} / {{ currentEntityPreviewLength }}
      </span>
      <button-simple
        class="button playlist-button flexrow-item"
        icon="right"
        :title="$t('playlists.actions.files_next')"
        :disabled="isPlaying"
        @click="onNextPreviewClicked"
      />
      <a
        class="button playlist-button flexrow-item"
        :href="currentPreviewPath"
        :title="$t('playlists.actions.see_original_file')"
        target="blank"
      >
        <arrow-up-right-icon class="icon is-small" />
      </a>
    </div>

    <div
      class="flexrow flexrow-item"
      v-if="isCurrentPreviewMovie"
    >
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x1.00"
        v-if="speed === 3"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x0.50"
        v-else-if="speed === 2"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onSpeedClicked"
        :title="$t('playlists.actions.speed')"
        text="x0.25"
        v-else
      />

      <button-simple
        class="flexrow-item playlist-button"
        :title="$t('playlists.actions.unmute')"
        icon="soundoff"
        @click="onToggleSoundClicked"
        v-if="isMuted"
      />
      <button-simple
        class="flexrow-item playlist-button"
        :title="$t('playlists.actions.mute')"
        icon="soundon"
        @click="onToggleSoundClicked"
        v-else
      />

      <button-simple
        class="button playlist-button flexrow-item"
        :active="isRepeating"
        :title="$t('playlists.actions.looping')"
        icon="repeat"
        @click="onRepeatClicked"
      />

      <span
        class="flexrow-item time-indicator"
        :title="$t('playlists.actions.current_time')"
      >
        {{ currentTime }}
      </span>
      <span class="flexrow-item time-indicator">
      /
      </span>
      <span
        class="flexrow-item time-indicator"
        :title="$t('playlists.actions.max_duration')"
      >
        {{ maxDuration }}
      </span>
      <span
        class="flexrow-item time-indicator mr1"
        :title="$t('playlists.actions.frame_number')"
      >
        ({{ currentFrame }})
      </span>
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onPreviousFrameClicked"
        :title="$t('playlists.actions.previous_frame')"
        icon="left"
      />
      <button-simple
        class="button playlist-button flexrow-item"
        @click="onNextFrameClicked"
        :title="$t('playlists.actions.next_frame')"
        icon="right"
      />
    </div>

    <div
      class="flexrow flexrow-item comparison-buttons"
      v-if="isCurrentPreviewMovie || isCurrentPreviewPicture"
    >
      <button-simple
        :class="{
          'comparison-button': true,
          'flexrow-item': true,
          'playlist-button': true,
          active: isComparing
        }"
        :title="$t('playlists.actions.split_screen')"
        icon="compare"
        @click="onCompareClicked"
        v-if="taskTypeOptions && taskTypeOptions.length > 0"
      />
      <div
        class="flexrow comparison-combos"
      >
        <combobox
          class="playlist-button flexrow-item comparison-list"
          :options="taskTypeOptions"
          v-model="taskTypeToCompare"
          @input="onTaskTypeToCompareChanged"
          v-if="isComparing"
        />
        <combobox
          class="playlist-button flexrow-item comparison-list"
          :options="revisionOptions"
          @input="onRevisionToCompareChanged"
          v-model="revisionToCompare"
          v-if="isComparing"
        />
        <combobox
          class="playlist-button flexrow-item comparison-list"
          :options="comparisonModeOptions"
          v-model="comparisonMode"
          @input="updatePlayingStatus"
          v-if="isComparing"
        />
        <div
          class="flexrow flexrow-item comparison-list"
          v-if="
            isComparing && currentRevisionToCompare &&
            currentComparisonPreviewLength > 1
          "
        >
          <button-simple
            class="button playlist-button flexrow-item"
            icon="left"
            @click="onPreviousComparisonPictureClicked"
          />
          <span class="flexrow-item comparison-index">
          {{ currentComparisonPreviewIndex + 1 }} / {{ currentComparisonPreviewLength }}
          </span>
          <button-simple
            class="button playlist-button flexrow-item"
            icon="right"
            @click="onNextComparisonPictureClicked"
          />
        </div>
        <div
          class="flexrow flexrow-item comparison-missing"
          v-if="isComparing && comparisonEntityMissing"
        >
          ⚠️  {{ $t('playlists.comparing_missing_plan') }}
        </div>
      </div>
    </div>
    <span class="filler"></span>

    <button-simple
      @click="$emit('save-clicked')"
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.save_playlist')"
      icon="save"
      v-if="isCurrentUserManager && tempMode"
    />
    <div
      class="flexrow"
      v-if="!isCurrentUserArtist && (isCurrentPreviewMovie || isCurrentPreviewPicture)"
    >
      <div
        class="separator"
        v-if="isCurrentUserManager && tempMode"
      ></div>
      <button-simple
        class="playlist-button flexrow-item"
        icon="undo"
        :title="$t('playlists.actions.annotation_undo')"
        @click="undoLastAction"
      />

      <button-simple
        class="playlist-button flexrow-item"
        :title="$t('playlists.actions.annotation_redo')"
        icon="redo"
        @click="redoLastAction"
      />

      <transition name="slide">
        <div
          class="annotation-tools"
          v-show="isTyping"
        >
          <color-picker
            :isOpen="isShowingPalette"
            :color="this.textColor"
            @TogglePalette="onPickColor"
            @change="onChangeTextColor"
          />
        </div>
      </transition>
      <button-simple
        :class="{
          'playlist-button': true,
          'flexrow-item': true,
          active: isTyping
        }"
        :title="$t('playlists.actions.annotation_text')"
        @click="onTypeClicked"
        icon="type"
      />

      <transition name="slide">
        <div
          class="annotation-tools"
          v-show="isDrawing"
        >
          <pencil-picker
            :isOpen="isShowingPencilPalette"
            :pencil="pencil"
            :sizes="this.pencilPalette"
            @toggle-palette="onPickPencil"
            @change="onChangePencil"
          />

          <color-picker
            :isOpen="isShowingPalette"
            :color="this.color"
            @TogglePalette="onPickColor"
            @change="onChangeColor"
          />
        </div>
      </transition>
      <button-simple
        :class="{
          'playlist-button': true,
          'flexrow-item': true,
          active: isDrawing
        }"
        :title="$t('playlists.actions.annotation_draw')"
        @click="onAnnotateClicked"
        icon="pencil"
      />
      <button-simple
        class="playlist-button flexrow-item"
        icon="remove"
        :title="$t('playlists.actions.annotation_delete')"
        @click="onDeleteClicked"
      />
    </div>
    <div class="separator"></div>
    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.change_task_type')"
      icon="layers"
      @click="showTaskTypeModal"
      v-if="!tempMode"
    />
    <button-simple
      class="button playlist-button flexrow-item"
      :title="$t('playlists.actions.comments')"
      @click="onCommentClicked"
      icon="comment"
    />
    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.entity_list')"
      @click="onFilmClicked"
      icon="film"
    />
    <button-simple
      class="playlist-button flexrow-item"
      :title="$t('playlists.actions.' + (isHd ? 'switch_ld' : 'switch_hd'))"
      :text="isHd ? 'HD' : 'LD'"
      @click="isHd = !isHd"
      v-if="isCurrentPreviewMovie"
    />

    <div
      class="flexrow-item playlist-button"
      style="position: relative"
      v-if="!tempMode"
    >
      <div
        :class="{
          'build-options': true,
          hidden: isDlButtonsHidden
        }"
      >
        <a
          class="dl-button zip-button"
          :href="zipDlPath"
        >
          {{ $t('playlists.download_zip') }}
        </a>
        <a
          class="dl-button zip-button"
          :href="csvDlPath"
        >
          {{ $t('playlists.download_csv') }}
        </a>
        <span
          :class="{
            'dl-button': true,
            'mp4-button': true,
            'disabled': !isCurrentUserManager || isJobRunning,
            hidden: isDlButtonsHidden
          }"
          @click="onBuildClicked"
        >
          {{ $t('playlists.build_mp4') }} - concat
        </span>
        <span
          :class="{
            'dl-button': true,
            'mp4-2-button': true,
            'disabled': !isCurrentUserManager || isJobRunning,
            hidden: isDlButtonsHidden
          }"
          @click="onBuildFullClicked"
        >
          {{ $t('playlists.build_mp4') }} - full
        </span>
      </div>
      <div
        :class="{
          'build-list': true,
          hidden: isDlButtonsHidden
        }"
      >
        <span v-if="!playlist.build_jobs || playlist.build_jobs.length === 0">
        {{ $t('playlists.no_build') }}
        </span>
        <div
          v-else
        >
          <div class="build-title">
            {{ $t('playlists.available_build') }}
          </div>
          <div
            class="flexrow"
            :key="job.id"
            v-for="job in playlist.build_jobs"
          >
            <spinner
              class="build-spinner"
              v-if="job.status === 'running'"
            />
            <span v-if="job.status === 'running'">
              {{ $t('playlists.building') }}
            </span>
            <span v-else-if="job.status === 'failed'">
              {{ $t('playlists.failed') }}
            </span>
            <a
              class="flexrow-item"
              :href="getBuildPath(job)"
              v-else
            >
              {{ formatDate(job.created_at) }}
            </a>
            <span class="filler"></span>
            <button
              class="delete-job-button"
              @click="onRemoveBuildJob(job)"
            >
              x
            </button>
          </div>
        </div>
      </div>
      <button-simple
        class="playlist-button"
        :title="$t('playlists.actions.download')"
        icon="download"
        @click="toggleDlButtons"
      />
    </div>

    <button-simple
      class="button playlist-button flexrow-item"
      :title="$t('playlists.actions.fullscreen')"
      @click="onFullscreenClicked"
      icon="maximize"
      v-if="isFullScreenEnabled"
    />
  </div>

  <div
    :class="{
      'playlisted-entities': true,
      flexrow: true,
      hidden: isEntitiesHidden
    }"
    ref="playlisted-entities"
    v-if="playlist.id"
  >
    <spinner class="spinner" v-if="isLoading" />
    <div
      class="flexrow-item has-text-centered playlisted-wrapper"
      :key="entity.id"
      v-for="(entity, index) in entityList"
      v-else
    >
      <playlisted-entity
        :ref="'entity-' + index"
        :index="index"
        :entity="entity"
        :is-playing="playingEntityIndex === index"
        @play-click="entityListClicked"
        @remove-entity="removeEntity"
        @preview-changed="onPreviewChanged"
        @entity-dropped="onEntityDropped"
      />
    </div>
  </div>

  <delete-modal
    :active="modals.delete"
    :is-loading="loading.deletePlaylist"
    :is-error="errors.deletePlaylist"
    :text="deleteText"
    :error-text="$t('playlists.delete_error')"
    @confirm="confirmRemovePlaylist"
    @cancel="hideDeleteModal"
  />

  <select-task-type-modal
    :active="modals.taskType"
    :task-type-list="entityTaskTypes"
    @confirm="confirmChangeTaskType"
    @cancel="hideTaskTypeModal"
  />

</div>
</template>

<script>
/*
 * This modules manages all the options available while playing a playlist.
 * It is made to work with a single playlist.
 */
import moment from 'moment-timezone'
import { mapActions, mapGetters } from 'vuex'
import { fabric } from 'fabric'
import { ArrowUpRightIcon, DownloadIcon } from 'vue-feather-icons'

import { formatFrame, formatTime, roundToFrame, floorToFrame } from '@/lib/video'
import ButtonSimple from '@/components/widgets/ButtonSimple'
import ColorPicker from '@/components/widgets/ColorPicker'
import Combobox from '@/components/widgets/Combobox'
import DeleteModal from '@/components/modals/DeleteModal'
import ObjectViewer from '@/components/previews/ObjectViewer'
import PencilPicker from '@/components/widgets/PencilPicker'
import PeopleAvatar from '@/components/widgets/PeopleAvatar'
import PlaylistedEntity from '@/components/pages/playlists/PlaylistedEntity'
import RawVideoPlayer from '@/components/pages/playlists/RawVideoPlayer'
import SelectTaskTypeModal from '@/components/modals/SelectTaskTypeModal'
import SoundViewer from '@/components/previews/SoundViewer'
import Spinner from '@/components/widgets/Spinner'
import TaskInfo from '@/components/sides/TaskInfo'
import VideoProgress from '@/components/previews/VideoProgress'

import { annotationMixin } from '@/components/mixins/annotation'
import { DEFAULT_NB_FRAMES_PICTURE } from '@/lib/playlist'
import { domMixin } from '@/components/mixins/dom'

export default {
  name: 'playlist-player',
  mixins: [annotationMixin, domMixin],

  components: {
    ArrowUpRightIcon,
    ButtonSimple,
    ColorPicker,
    Combobox,
    DownloadIcon,
    DeleteModal,
    ObjectViewer,
    PencilPicker,
    PeopleAvatar,
    PlaylistedEntity,
    RawVideoPlayer,
    SelectTaskTypeModal,
    SoundViewer,
    Spinner,
    TaskInfo,
    VideoProgress
  },

  props: {
    playlist: {
      type: Object,
      default: () => {}
    },
    entities: {
      type: Object,
      default: () => {}
    },
    isLoading: {
      type: Boolean,
      default: false
    },
    isAddingEntity: {
      type: Boolean,
      default: false
    },
    isAssetPlaylist: {
      type: Boolean,
      default: false
    },
    tempMode: {
      type: Boolean,
      default: false
    }
  },

  data () {
    return {
      annotations: [],
      buildLaunched: false,
      color: '#ff3860',
      comparisonEntityMissing: false,
      comparisonMode: 'sidebyside',
      currentComparisonPreviewIndex: 0,
      currentPreviewIndex: 0,
      currentTime: '00:00.000',
      currentTimeRaw: 0,
      entityList: [],
      entityListToCompare: [],
      fabricCanvas: null,
      framesPerImage: [],
      framesSeenOfPicture: 0,
      fullScreen: false,
      isBuildLaunched: false,
      isCommentsHidden: true,
      isComparing: false,
      isDlButtonsHidden: true,
      isDrawing: false,
      isEntitiesHidden: false,
      isHd: false,
      isMuted: false,
      isPlaying: false,
      isRepeating: false,
      isShowingPalette: false,
      isShowingPencilPalette: false,
      isTyping: false,
      maxDuration: '00:00.000',
      maxDurationRaw: 0,
      onNextTimeUpdateActions: [],
      pencil: 'big',
      pencilPalette: ['big', 'medium', 'small'],
      playlistToEdit: {},
      playingEntityIndex: 0,
      revisionOptions: [],
      savedTaskTypeToCompare: null,
      room: {
        people: [],
        newComer: true
      },
      speed: 3,
      task: null,
      taskTypeOptions: [],
      taskTypeToCompare: null,
      revisionToCompare: null,
      textColor: '#ff3860',
      modals: {
        delete: false,
        taskType: false
      },
      loading: {
        deletePlaylist: false
      },
      errors: {
        playlists: false,
        deletePlaylist: false
      },
      forClientOptions: [
        { label: this.$t('playlists.for_client'), value: 'true' },
        { label: this.$t('playlists.for_studio'), value: 'false' }
      ]
    }
  },

  mounted () {
    this.$options.scrubbing = false
    this.isHd = this.organisation
      ? this.organisation.hd_by_default === 'true'
      : false
    if (this.entities) {
      this.entityList = Object.values(this.entities)
    } else {
      this.entityList = []
    }
    if (this.picturePlayer) {
      this.picturePlayer.addEventListener('load', async () => {
        const wasPlaying = this.isPlaying
        await this.resetPictureCanvas()
        this.isPlaying = wasPlaying
      })
    }
    this.$nextTick(() => {
      this.configureEvents()
      this.setupFabricCanvas()
      this.resetCanvas()
      this.setPlayerSpeed(1)
      this.rebuildComparisonOptions()
      this.onFrameUpdate(1)
    })
  },

  beforeDestroy () {
    this.endAnnotationSaving()
    this.removeEvents()
    this.leaveRoom()
  },

  computed: {
    ...mapGetters([
      'assetTaskTypes',
      'currentEpisode',
      'currentProduction',
      'isCurrentUserArtist',
      'isCurrentUserClient',
      'isCurrentUserManager',
      'isTVShow',
      'organisation',
      'personEmailMap',
      'personMap',
      'previewFileMap',
      'taskMap',
      'taskTypeMap',
      'shotTaskTypes',
      'user'
    ]),

    joinedRoom () {
      return !!this.room.people.find(email => email === this.user.email)
    },

    extension () {
      if (!this.currentPreview) return ''
      if (this.currentPreview.extension) {
        return this.currentPreview.extension
      }
      return ''
    },

    isCurrentPreviewMovie () {
      return this.extension === 'mp4'
    },

    isCurrentPreviewPicture () {
      return this.isPicture(this.extension)
    },

    isCurrentPreviewModel () {
      return this.isModel(this.extension)
    },

    isCurrentPreviewSound () {
      return this.isSound(this.extension)
    },

    isCurrentPreviewFile () {
      return (
        !this.isCurrentPreviewMovie &&
        !this.isCurrentPreviewPicture &&
        !this.isCurrentPreviewSound &&
        !this.isCurrentPreviewModel
      )
    },

    isMovieComparison () {
      if (!this.currentPreviewToCompare) return false
      return this.currentPreviewToCompare.extension === 'mp4'
    },

    isPictureComparison () {
      if (!this.currentPreviewToCompare) return false
      return (
        this.isPicture(this.currentPreviewToCompare.extension)
      )
    },

    isComparisonOverlay () {
      return this.comparisonMode !== 'sidebyside'
    },

    overlayOpacity () {
      if (this.isComparing && this.isComparisonOverlay) {
        switch (this.comparisonMode) {
          case 'overlay25':
            return 0.25
          case 'overlay50':
            return 0.5
          case 'overlay75':
            return 0.75
          default:
            return 1
        }
      } else {
        return 1
      }
    },

    comparisonModeOptions () {
      return [
        {
          label: this.$t('playlists.actions.side_by_side'),
          value: 'sidebyside'
        },
        {
          label: `${this.$t('playlists.actions.overlay')} 25%`,
          value: 'overlay25'
        },
        {
          label: `${this.$t('playlists.actions.overlay')} 50%`,
          value: 'overlay50'
        },
        {
          label: `${this.$t('playlists.actions.overlay')} 75%`,
          value: 'overlay75'
        }
      ]
    },

    currentRevisionToCompare () {
      if (!this.currentEntity) return null
      const previewFiles =
        this.currentEntity.preview_files[this.taskTypeToCompare]
      if (previewFiles && previewFiles.length > 0) {
        const preview =
          previewFiles.find(p => `${p.revision}` === this.revisionToCompare)
        if (preview) return preview
        else {
          return previewFiles[0]
        }
      } else {
        return null
      }
    },

    currentPreviewToCompare () {
      if (!this.currentEntity) return null
      if (
        this.currentComparisonPreviewIndex > 0
      ) {
        const index = this.currentComparisonPreviewIndex - 1
        return this.currentRevisionToCompare.previews[index]
      } else {
        return this.currentRevisionToCompare
      }
    },

    currentPreviewPath () {
      if (this.currentPreview) {
        let previewId = this.currentPreview.id
        let extension = this.currentPreview.extension
        if (this.currentPreviewIndex > 0) {
          const index = this.currentPreviewIndex - 1
          const preview = this.currentEntity.preview_file_previews[index]
          previewId = preview.id
          extension = preview.extension
        }
        return `/api/pictures/originals/preview-files/${previewId}.${extension}`
      } else {
        return ''
      }
    },

    currentComparisonPreviewPath () {
      if (this.currentPreviewToCompare && this.isPictureComparison) {
        const extension = this.currentPreviewToCompare.extension
        const previewId = this.currentPreviewToCompare.id
        return `/api/pictures/originals/preview-files/${previewId}.${extension}`
      } else {
        return ''
      }
    },

    currentPreviewOriginalPath () {
      if (!this.currentPreview) return ''
      const previewId = this.currentPreview.id
      const extension = this.currentPreview.extension
      return `/api/pictures/originals/preview-files/${previewId}.${extension}`
    },

    currentPreviewDlPath () {
      if (!this.currentPreview) return ''
      const previewId = this.currentPreview.id
      return `/api/pictures/originals/preview-files/${previewId}/download`
    },

    currentEntity () {
      return this.entityList[this.playingEntityIndex]
    },

    currentPreview () {
      if (!this.currentEntity) return null
      if (this.currentPreviewIndex === 0) {
        return {
          id: this.currentEntity.preview_file_id,
          extension: this.currentEntity.preview_file_extension,
          task_id: this.currentEntity.preview_file_task_id,
          annotations: this.currentEntity.preview_file_annotations || []
        }
      } else {
        return this.currentEntity.preview_file_previews[
          this.currentPreviewIndex - 1
        ]
      }
    },

    previousEntityIndex () {
      let index = this.playingEntityIndex - 1
      if (index < 0) index = this.entityList.length - 1
      return index
    },

    nextEntityIndex () {
      let index = this.playingEntityIndex + 1
      if (index > this.entityList.length - 1) index = 0
      return index
    },

    currentEntityPreviewLength () {
      if (!this.currentEntity || !this.currentEntity.preview_file_previews) {
        return 0
      }
      return this.currentEntity.preview_file_previews.length + 1
    },

    currentComparisonPreviewLength () {
      if (this.currentRevisionToCompare) {
        const previews = this.currentRevisionToCompare.previews
        return previews ? previews.length + 1 : 0
      } else {
        return 0
      }
    },

    isFullScreenEnabled () {
      return !!(
        document.fullscreenEnabled ||
        document.mozFullScreenEnabled ||
        document.msFullscreenEnabled ||
        document.webkitSupportsFullscreen ||

        document.webkitFullscreenEnabled ||
        document.createElement('video').webkitRequestFullScreen
      )
    },

    csvDlPath () {
      return `/api/export/csv/playlists/${this.playlist.id}`
    },

    zipDlPath () {
      return `/api/data/playlists/${this.playlist.id}/download/zip`
    },

    frameNumber () {
      let frameNumber = this.currentTimeRaw / this.frameDuration
      if (frameNumber >= this.nbFrames) {
        frameNumber = this.nbFrames
      }
      return Math.round(frameNumber) - 1
    },

    currentFrame () {
      return formatFrame(this.frameNumber + 2)
    },

    currentFrameMovieOrPicture () {
      if (this.isCurrentPreviewMovie) {
        return parseInt(this.currentFrame)
      } else if (this.isCurrentPreviewPicture) {
        return this.framesSeenOfPicture
      }
      return 0
    },

    deleteText () {
      if (this.playlist) {
        return this.$t('playlists.delete_text', { name: this.playlist.name })
      } else {
        return ''
      }
    },

    timezone () {
      return this.user.timezone || moment.tz.guess()
    },

    entityTaskTypes () {
      if (this.playlist.for_entity === 'asset') {
        return this.assetTaskTypes
      } else {
        return this.shotTaskTypes
      }
    },

    addEntitiesText () {
      if (this.isAssetPlaylist) {
        return this.$t('playlists.add_assets')
      } else {
        return this.$t('playlists.add_shots')
      }
    },

    frameDuration () {
      return Math.round((1 / this.fps) * 10000) / 10000
    },

    fps () {
      return this.currentProduction
        ? parseInt(this.currentProduction.fps || '24')
        : 24
    },

    container () {
      return this.$refs.container
    },

    rawPlayer () {
      return this.$refs['raw-player']
    },

    rawPlayerComparison () {
      return this.$refs['raw-player-comparison']
    },

    picturePlayer () {
      return this.$refs['picture-player']
    },

    soundPlayer () {
      return this.$refs['sound-player']
    },

    canvas () {
      return this.$refs['canvas-wrapper']
    },

    progress () {
      return this.$refs['video-progress']
    },

    video () {
      return this.$refs.movie
    },

    isJobRunning () {
      return this.playlist.build_jobs
        .filter(job => job.status === 'running')
        .length !== 0
    },

    nbFrames () {
      return Math.round(this.maxDurationRaw * this.fps)
    }
  },

  methods: {
    ...mapActions([
      'changePlaylistType',
      'deletePlaylist',
      'refreshPreview',
      'removeBuildJob',
      'runPlaylistBuild'
    ]),

    isMovie (extension) {
      return extension === 'mp4'
    },

    isPicture (extension) {
      return ['png', 'gif'].includes(extension)
    },

    isModel (extension) {
      return ['glb', 'gltf'].includes(extension)
    },

    isSound (extension) {
      return ['mp3', 'wav'].includes(extension)
    },

    exists (variable) {
      return variable !== null && variable !== undefined
    },

    configureEvents () {
      window.addEventListener('keydown', this.onKeyDown, false)
      window.addEventListener('resize', this.onWindowResize)
      if (!this.$el.nomousemove) this.$el.onmousemove = this.onMouseMove
      this.container.addEventListener(
        'fullscreenchange', this.onFullScreenChange, false)
      this.container.addEventListener(
        'mozfullscreenchange', this.onFullScreenChange, false)
      this.container.addEventListener(
        'MSFullscreenChange', this.onFullScreenChange, false)
      this.container.addEventListener(
        'webkitfullscreenchange', this.onFullScreenChange, false)
      window.addEventListener('beforeunload', this.onWindowsClosed)
    },

    removeEvents () {
      window.removeEventListener('keydown', this.onKeyDown)
      window.removeEventListener('resize', this.onWindowResize)
      window.removeEventListener('beforeunload', this.onWindowsClosed)
      this.$el.onmousemove = null
      this.container.removeEventListener(
        'fullscreenchange', this.onFullScreenChange, false)
      this.container.removeEventListener(
        'mozfullscreenchange', this.onFullScreenChange, false)
      this.container.removeEventListener(
        'MSFullscreenChange', this.onFullScreenChange, false)
      this.container.removeEventListener(
        'webkitfullscreenchange', this.onFullScreenChange, false)
    },

    getBuildPath (job) {
      return `/api/data/playlists/${this.playlist.id}/jobs/${job.id}/build/mp4`
    },

    formatDate (creationDate) {
      const date = moment.tz(creationDate, 'UTC').tz(this.timezone)
      return date.format('YYYY-MM-DD HH:mm')
    },

    formatFrame,

    formatTime,

    displayBars () {
      if (this.$refs['button-bar']) {
        if (this.$refs.header) {
          this.$refs.header.style.opacity = 1
        }
        if (this.$refs['button-bar']) {
          this.$refs['button-bar'].style.opacity = 1
        }
        if (this.$refs['video-progress']) {
          this.$refs['video-progress'].$el.style.opacity = 1
        }
        this.container.style.cursor = 'default'
      }
    },

    hideBars () {
      this.$refs.header.style.opacity = 0
      this.$refs['button-bar'].style.opacity = 0
      this.$refs['video-progress'].$el.style.opacity = 0
    },

    showDeleteModal () {
      this.modals.delete = true
    },

    hideDeleteModal () {
      this.modals.delete = false
    },

    confirmRemovePlaylist () {
      this.loading.deletePlaylist = true
      this.errors.deletePlaylist = false
      this.deletePlaylist({
        playlist: this.playlist,
        callback: (err) => {
          if (err) this.errors.deletePlaylist = true
          this.loading.deletePlaylist = false
          this.$emit('playlist-deleted')
          this.modals.delete = false
        }
      })
    },

    updateProgressBar () {
      if (this.progress) {
        this.progress.updateProgressBar(this.frameNumber + 1)
      }
    },

    updateTaskPanel () {
      if (this.entityList.length > 0) {
        const entity = this.entityList[this.playingEntityIndex]
        if (entity) this.task = this.taskMap.get(entity.preview_file_task_id)
        else this.task = null
      } else {
        this.task = null
      }
    },

    scrollToEntity (index) {
      const entityEl = this.$refs['entity-' + index]
      if (entityEl && entityEl[0]) {
        const entityWidget = entityEl[0].$el
        const playlistEl = this.$refs['playlisted-entities']
        const entity = this.entityList[index]
        this.annotations = entity.preview_file_annotations || []
        if (entityWidget) {
          const margin = 30
          const rect = entityWidget.getBoundingClientRect()
          const listRect = playlistEl.getBoundingClientRect()
          const isRight = rect.right > listRect.right - margin
          const isLeft = rect.left < listRect.left - margin

          if (isLeft) {
            const scrollingRequired = rect.left - listRect.left - margin
            playlistEl.scrollLeft = playlistEl.scrollLeft + scrollingRequired
          } else if (isRight) {
            const scrollingRequired = rect.right - listRect.right + margin
            playlistEl.scrollLeft = playlistEl.scrollLeft + scrollingRequired
          }
        }
      }
    },

    scrollToRight () {
      if (this.entityList.length > 0) {
        this.scrollToEntity(this.entityList.length - 1)
      }
    },

    playClicked () {
      this.play()
      this.updatePlayingStatus()
    },

    pauseClicked () {
      this.pause()
      this.updatePlayingStatus()
    },

    play () {
      if (this.isCurrentPreviewPicture) {
        this.playPicture()
      } else if (this.isCurrentPreviewSound) {
        this.playSound()
      } else {
        this.rawPlayer.play()
        if (this.isComparing) {
          this.rawPlayerComparison.play()
        }
        this.isPlaying = this.rawPlayer.isPlaying
      }
      this.hideCanvas()
      this.clearCanvas()
    },

    pause () {
      this.showCanvas()
      if (this.isCurrentPreviewMovie) {
        const comparisonPlayer = this.$refs['raw-player-comparison']
        if (this.rawPlayer) this.rawPlayer.pause()
        if (comparisonPlayer) comparisonPlayer.pause()
      } else if (this.isCurrentPreviewSound) {
        this.soundPlayer.pause()
      }
      this.isPlaying = false
    },

    entityListClicked (entityIndex) {
      this.playEntity(entityIndex)
      this.updatePlayingStatus()
    },

    playEntity (entityIndex) {
      const entity = this.entityList[entityIndex]
      const wasDrawing = this.isDrawing === true
      this.hideCanvas()
      this.clearCanvas()
      this.framesSeenOfPicture = 0
      this.playingEntityIndex = entityIndex
      if (entity && this.isMovie(entity.preview_file_extension)) {
        this.$nextTick(() => {
          this.scrollToEntity(this.playingEntityIndex)
          this.rawPlayer.loadEntity(entityIndex)
          this.annotations = entity.preview_file_annotations || []
          if (this.isComparing) {
            this.$refs['raw-player-comparison'].loadEntity(entityIndex)
          }
          if (this.isPlaying) {
            this.rawPlayer.play()
            if (this.isComparing) this.$refs['raw-player-comparison'].play()
          } else {
            this.showCanvas()
          }
        })
      } else {
        const annotation = this.getAnnotation(0)
        if (!this.isPlaying) this.loadAnnotation(annotation)
        if (wasDrawing) {
          setTimeout(() => {
            this.isDrawing = true
            this.fabricCanvas.isDrawingMode = true
          }, 100)
        }
        if (this.isPlaying && this.isPicture(entity.preview_file_extension)) {
          this.playPicture()
        }
      }
      this.scrollToEntity(this.playingEntityIndex)
    },

    syncComparisonPlayer () {
      if (
        this.rawPlayerComparison &&
        this.isComparing &&
        this.rawPlayerComparison.currentPlayer
      ) {
        const currentTimeRaw = this.rawPlayer.getCurrentTimeRaw()
        this.rawPlayerComparison.currentPlayer.currentTime = currentTimeRaw
      }
    },

    goPreviousFrame () {
      this.clearCanvas()
      this.rawPlayer.goPreviousFrame()
      if (this.isComparing) this.syncComparisonPlayer()
      const annotation = this.getAnnotation(
        this.rawPlayer.getCurrentTime() - this.frameDuration)
      if (annotation) this.loadSingleAnnotation(annotation)
    },

    goNextFrame () {
      this.clearCanvas()
      this.rawPlayer.goNextFrame()
      if (this.isComparing) this.syncComparisonPlayer()
      const annotation = this.getAnnotation(
        this.rawPlayer.getCurrentTime() - this.frameDuration)
      if (annotation) this.loadSingleAnnotation(annotation)
    },

    removeEntity (entity) {
      this.$emit('remove-entity', entity)
      this.$options.silent = true
      const entityIndex = this.entityList.findIndex(s => s.id === entity.id)
      this.entityList.splice(entityIndex, 1)
      setTimeout(() => {
        this.$options.silent = false
      }, 1000)
    },

    setFullScreen () {
      if (this.container.requestFullscreen) {
        this.container.requestFullscreen()
      } else if (this.container.mozRequestFullScreen) {
        this.container.mozRequestFullScreen()
      } else if (this.container.webkitRequestFullScreen) {
        this.container.webkitRequestFullScreen()
      } else if (this.container.msRequestFullscreen) {
        this.container.msRequestFullscreen()
      }
      this.container.setAttribute('data-fullscreen', !!true)
      document.activeElement.blur()
      this.fullScreen = true
    },

    exitFullScreen () {
      if (document.exitFullscreen) {
        document.exitFullscreen()
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen()
      } else if (document.webkitCancelFullScreen) {
        document.webkitCancelFullScreen()
      } else if (document.msExitFullscreen) {
        document.msExitFullscreen()
      }
      this.container.setAttribute('data-fullscreen', !!false)
      document.activeElement.blur()
      this.fullScreen = false
    },

    isFullScreen () {
      return !!(
        document.fullScreen ||
        document.webkitIsFullScreen ||
        document.mozFullScreen ||
        document.msFullscreenElement ||
        document.fullscreenElement
      )
    },

    onProgressChanged (frameNumber) {
      this.clearCanvas()
      this.rawPlayer.setCurrentFrame(frameNumber)
      this.syncComparisonPlayer()
      const annotation = this.getAnnotation(
        frameNumber * this.frameDuration
      )
      if (annotation) this.loadAnnotation(annotation)
      this.sendUpdatePlayingStatus()
      this.onFrameUpdate(frameNumber)
    },

    onPreviousFrameClicked () {
      this.clearFocus()
      this.goPreviousFrame()
      this.sendUpdatePlayingStatus()
    },

    onNextFrameClicked () {
      this.clearFocus()
      this.goNextFrame()
      this.sendUpdatePlayingStatus()
    },

    onPlayPreviousEntityClicked (forcePlay = false) {
      this.clearFocus()
      this.playEntity(this.previousEntityIndex)
      this.sendUpdatePlayingStatus()
    },

    onPlayNextEntity (forcePlay = false) {
      this.clearFocus()
      this.playEntity(this.nextEntityIndex)
      this.sendUpdatePlayingStatus()
    },

    onPlayNextEntityClicked (forcePlay = false) {
      this.onPlayNextEntity(forcePlay)
      this.sendUpdatePlayingStatus()
    },

    onPlayPauseClicked () {
      this.clearFocus()
      if (!this.isPlaying) {
        this.playClicked()
      } else {
        this.pauseClicked()
        const annotation = this.getAnnotation(this.rawPlayer.getCurrentTime())
        if (annotation) this.loadAnnotation(annotation)
      }
    },

    onVideoRepeated () {
      if (!this.isCommentsHidden && !this.isFocusTextarea()) {
        this.clearFocus()
      }
      if (this.rawPlayerComparison) {
        this.syncComparisonPlayer()
        this.rawPlayerComparison.play()
      }
    },

    onRepeatClicked () {
      this.clearFocus()
      this.isRepeating = !this.isRepeating
      this.updatePlayingStatus()
    },

    onToggleSoundClicked () {
      this.clearFocus()
      this.isMuted = !this.isMuted
    },

    onFullScreenChange () {
      if (
        this.fullScreen &&
        !this.isFullScreen()
      ) {
        this.fullScreen = false
      }
    },

    onFullscreenClicked () {
      /** @lends fabric.IText.prototype */
      // fix for : IText not editable when canvas is in a fullscreen
      // element on chrome
      // https://github.com/fabricjs/fabric.js/issues/5126
      const originalInitHiddenTextarea =
        fabric.IText.prototype.initHiddenTextarea
      if (this.isFullScreen()) {
        fabric.util.object.extend(fabric.IText.prototype, {
          initHiddenTextarea: function () {
            originalInitHiddenTextarea.call(this)
            fabric.document.body.appendChild(this.hiddenTextarea)
          }
        })
        this.exitFullScreen()
      } else {
        fabric.util.object.extend(fabric.IText.prototype, {
          initHiddenTextarea: function () {
            originalInitHiddenTextarea.call(this)
            this.canvas.wrapperEl.appendChild(this.hiddenTextarea)
          }
        })
        this.setFullScreen()
      }
    },

    onKeyDown (event) {
      this.displayBars()
      if (!['INPUT', 'TEXTAREA'].includes(event.target.tagName)) {
        if (event.keyCode === 46 && this.fabricCanvas) {
          this.deleteSelection()
        } else if (event.keyCode === 37) {
          event.preventDefault()
          event.stopPropagation()
          this.onPreviousFrameClicked()
        } else if (event.keyCode === 39) {
          event.preventDefault()
          event.stopPropagation()
          this.onNextFrameClicked()
        } else if (event.keyCode === 32) {
          event.preventDefault()
          event.stopPropagation()
          this.onPlayPauseClicked()
        } else if (event.altKey && event.keyCode === 74) { // alt+j
          event.preventDefault()
          event.stopPropagation()
          this.onPlayPreviousEntityClicked()
        } else if (event.altKey && event.keyCode === 75) { // alt+k
          event.preventDefault()
          event.stopPropagation()
          this.onPlayNextEntityClicked()
        } else if (event.ctrlKey && event.keyCode === 67) { // ctrl + c
          this.copyAnnotations()
        } else if (event.ctrlKey && event.keyCode === 86) { // ctrl + v
          this.pasteAnnotations()
        } else if (event.ctrlKey && event.altKey && event.keyCode === 68) {
          this.onAnnotateClicked()
        } else if (event.ctrlKey && event.keyCode === 90) {
          this.undoLastAction()
        } else if (event.altKey && event.keyCode === 82) {
          this.redoLastAction()
        }
      }
    },

    onWindowResize () {
      const now = (new Date().getTime())
      this.lastCall = this.lastCall || 0
      if (now - this.lastCall > 600) {
        this.lastCall = now
        setTimeout(() => {
          this.resetHeight()
          this.resetCanvas()
            .then(() => {
              this.reloadAnnotations()
            })
        }, 200)
      }
    },

    reloadAnnotations () {
      if (!this.annotations) return
      const annotations = this.annotations.map(a => ({ ...a }))
      this.annotations = []
      setTimeout(() => {
        this.annotations = annotations
        this.reloadCurrentAnnotation()
      }, 200)
    },

    onFilmClicked () {
      this.isEntitiesHidden = !this.isEntitiesHidden
      this.$nextTick(() => {
        this.resetHeight()
        this.reloadAnnotations()
        this.scrollToEntity(this.playingEntityIndex)
      })
    },

    getCurrentTime () {
      return roundToFrame(this.currentTimeRaw, this.fps) || 0
    },

    reloadCurrentAnnotation () {
      let currentTime = roundToFrame(this.currentTimeRaw, this.fps) || 0
      if (this.isCurrentPreviewPicture) currentTime = 0
      const annotation = this.getAnnotation(currentTime)
      if (annotation) this.loadAnnotation(annotation)
    },

    onCommentClicked () {
      const height = this.$refs['video-container'].offsetHeight
      this.isCommentsHidden = !this.isCommentsHidden
      if (!this.isCommentsHidden) {
        this.$refs['task-info'].$el.style.height = `${height}px`
      }
      this.$nextTick(() => {
        this.$refs['task-info'].focusCommentTextarea()
        this.resetHeight()
        this.reloadAnnotations()
      })
    },

    onCompareClicked () {
      this.isComparing = !this.isComparing
      this.$nextTick(() => {
        this.saveUserComparisonChoice()
        this.comparisonEntityMissing = false
      })
      this.updatePlayingStatus()
    },

    onSpeedClicked () {
      this.speed = this.speed + 1 > 3 ? 1 : this.speed + 1
      let rate = 1
      if (this.speed === 2) rate = 0.5
      if (this.speed === 1) rate = 0.25
      this.setPlayerSpeed(rate)
    },

    setPlayerSpeed (rate) {
      this.rawPlayer.setSpeed(rate)
      this.rawPlayerComparison.setSpeed(rate)
    },

    onFrameUpdate (frame) {
      this.currentTimeRaw = frame * this.frameDuration
      this.currentTime = this.formatTime(this.currentTimeRaw)
      this.updateProgressBar()
      const actions = this.onNextTimeUpdateActions
      actions.forEach(action => action())
      this.onNextTimeUpdateActions = []
    },

    onMaxDurationUpdate (duration) {
      if (duration) {
        duration = floorToFrame(duration, this.fps)
        this.maxDurationRaw = duration
        this.maxDuration = this.formatTime(duration)
      } else {
        this.maxDurationRaw = 0
        this.maxDuration = '00.00.000'
      }
    },

    onMouseMove () {
      const buttonBar = this.$refs['button-bar']
      if (buttonBar && buttonBar.style.opacity !== 1) {
        this.displayBars()
      }
      const isMovieFullScreen =
        this.isFullScreen() && this.isEntitiesHidden && this.isCommentsHidden
      if (isMovieFullScreen) {
        if (this.timer) clearTimeout(this.timer)
        this.timer = setTimeout(() => {
          const isMovieFullScreen =
            this.isFullScreen() && this.isEntitiesHidden && this.isCommentsHidden
          if (isMovieFullScreen) this.hideBars()
        }, 2000)
      }
    },

    onPlayNext () {
      const nextEntity = this.entityList[this.nextEntityIndex]
      if (nextEntity.preview_file_extension === 'mp4') {
        this.rawPlayer.playNext()
      } else if (this.isRepeating && this.isCurrentPreviewMovie) {
        this.rawPlayer.playNext()
      } else {
        this.onPlayNextEntityClicked()
        if (this.isCurrentPreviewPicture) {
          this.framesSeenOfPicture = 0
          this.playPicture()
        }
      }
    },

    onPlayerEntityChange (entityIndex) {
      if (this.isCurrentPreviewMovie) {
        this.playingEntityIndex = entityIndex
        if (this.isComparing) {
          const comparisonIndex = this.rawPlayerComparison.currentIndex
          if (comparisonIndex !== entityIndex) {
            this.rawPlayerComparison.playNext()
          }
        }
      }
      if (!this.$options.silent) this.scrollToEntity(this.playingEntityIndex)
    },

    playPicture () {
      if (this.isPlaying) clearTimeout(this.playingPictureTimeout)
      this.isPlaying = true
      this.playingPictureTimeout = setTimeout(
        this.continuePlayingPlaylist,
        100,
        this.playingEntityIndex,
        Date.now() - 1000 * this.framesSeenOfPicture / this.fps
      )
    },

    playSound () {
      this.isPlaying = true
      this.soundPlayer.play()
    },

    continuePlayingPlaylist (entityIndex, startMs) {
      const framesPerImage = this.framesPerImage[entityIndex]
      const durationToWaitMs = framesPerImage * 1000 / this.fps
      const durationWaited = Date.now() - startMs
      if (!this.isPlaying) return
      else if (durationWaited < durationToWaitMs) {
        this.framesSeenOfPicture = Math.floor(
          (durationWaited / 1000) * this.fps
        )
        this.playingPictureTimeout = setTimeout(
          this.continuePlayingPlaylist, 100, entityIndex, startMs
        )
        return
      }

      // we've seen all the frames the picture should be visible
      this.framesSeenOfPicture = 0
      const previews = this.currentEntity.preview_file_previews
      if (previews.length === this.currentPreviewIndex) {
        this.$nextTick(() => {
          this.onPlayNextEntity(true)
        })
      } else {
        this.currentPreviewIndex++
        this.$nextTick(() => {
          this.playingPictureTimeout = setTimeout(
            this.continuePlayingPlaylist,
            100,
            this.playingEntityIndex,
            Date.now()
          )
        })
      }
    },

    onPreviewChanged (entity, previewFile) {
      this.pause()
      const localEntity = this.entityList.find(s => s.id === entity.id)
      localEntity.preview_file_id = previewFile.id
      localEntity.preview_file_task_id = previewFile.task_id
      localEntity.preview_file_extension = previewFile.extension
      localEntity.preview_file_annotations = previewFile.annotations
      localEntity.preview_file_previews = previewFile.previews
      if (this.rawPlayer) this.rawPlayer.reloadCurrentEntity()
      this.$emit('preview-changed', entity, previewFile.id)
      this.clearCanvas()
      this.updateTaskPanel()
    },

    onEntityDropped (info) {
      const playlistEl = this.$refs['playlisted-entities']
      const scrollLeft = playlistEl.scrollLeft

      const entityToMove = this.entityList.find(s => s.id === info.after)
      const toMoveIndex = this.entityList.findIndex(s => s.id === info.after)
      let targetIndex = this.entityList.findIndex(s => s.id === info.before)
      if (toMoveIndex >= 0 && targetIndex >= 0) {
        this.entityList.splice(toMoveIndex, 1)
        if (toMoveIndex > targetIndex) targetIndex++
        this.entityList.splice(targetIndex, 0, entityToMove)
      }

      this.$nextTick(() => {
        playlistEl.scrollLeft = scrollLeft
      })
      this.$emit('order-change', info)
    },

    resetHeight () {
      this.$nextTick(() => {
        let height = window.innerHeight - 90
        if (!this.tempMode) {
          height = this.container ? this.container.offsetHeight : 0
        }
        height -= this.$refs.header ? this.$refs.header.offsetHeight : 0
        if (this.$refs['button-bar']) {
          height -= this.$refs['button-bar'].offsetHeight
        }
        if (this.$refs['playlisted-entities']) {
          height -= this.$refs['playlisted-entities'].offsetHeight
        }
        if (this.$refs['video-progress']) {
          height -= this.$refs['video-progress'].$el.offsetHeight
        }
        if (this.$refs['video-container']) {
          this.$refs['video-container'].style.height = `${height}px`
        }
        if (!this.isCommentsHidden) {
          this.$refs['task-info'].$el.style.height = `${height}px`
        }
        if (this.rawPlayer) this.rawPlayer.resetHeight(height)
        if (this.isComparing) {
          this.$refs['raw-player-comparison'].resetHeight(height)
          if (this.$refs['picture-preview-wrapper']) {
            this.$refs['picture-preview-wrapper'].style.height = `${height}px`
          }
        }
        this.$nextTick(() => {
          this.resetCanvas()
          this.updateProgressBar()
        })
      })
    },

    resetCanvas () {
      this.clearCanvas()
      return this.resetCanvasSize()
        .then(() => {
          if (this.fabricCanvas) this.fabricCanvas.renderAll()
          return Promise.resolve(this.fabricCanvas)
        })
    },

    resetCanvasSize () {
      return this.$nextTick()
        .then(() => {
          if (this.isCurrentPreviewMovie && this.fabricCanvas) {
            if (this.canvas) {
              // Video Ratio
              const ratio = this.rawPlayer.getVideoRatio()

              // Container size
              const fullWidth = this.rawPlayer.$el.offsetWidth
              const fullHeight = this.rawPlayer.$el.offsetHeight
              const width = ratio ? fullHeight * ratio : fullWidth

              if (fullWidth > width) {
                // Case where canvas is less big than the container
                const left = Math.round((fullWidth - width) / 2)
                this.canvas.style.left = left + 'px'
                this.canvas.style.top = '0px'
                this.fabricCanvas.setDimensions({ width, height: fullHeight })
              } else {
                // Case where canvas is bigger than the container
                const height = ratio ? Math.round(fullWidth / ratio) : fullHeight
                const top = Math.round((fullHeight - height) / 2)
                this.canvas.style.left = '0px'
                this.canvas.style.top = top + 'px'
                this.fabricCanvas.setDimensions({ width: fullWidth, height })
              }
            }
          } else if (this.isCurrentPreviewPicture && this.fabricCanvas) {
            if (this.canvas) {
              // Picture ratio
              const naturalWidth = this.picturePlayer.naturalWidth
              const naturalHeight = this.picturePlayer.naturalHeight
              const ratio = naturalWidth / naturalHeight

              if (!this.$refs['video-container']) return Promise.resolve()

              // Container size
              let fullWidth = this.$refs['video-container'].offsetWidth
              const fullHeight = this.$refs['video-container'].offsetHeight
              if (this.isComparing && !this.isComparisonOverlay) {
                fullWidth = Math.round(fullWidth / 2)
              }

              // Init canvas values
              let width = ratio ? fullHeight * ratio : fullWidth
              let height = ratio ? Math.round(fullWidth / ratio) : fullHeight
              let top = 0
              let left = 0
              this.canvas.style.top = '0px'
              this.canvas.style.left = '0px'

              // Set Canvas width and left position
              if (fullWidth > naturalWidth) {
                // Case where picture is less wide than the container
                // We adapt left position, because there will be margins
                left = Math.round((fullWidth - naturalWidth) / 2)
                this.canvas.style.left = left + 'px'
                width = naturalWidth
              } else if (fullWidth > width) {
                // Case where canvas is less wide than the container
                // We adapt left position
                const left = Math.round((fullWidth - width) / 2)
                this.canvas.style.left = left + 'px'
              } else {
                // Case where canvas is wider than the container
                // We set the width to the container size
                width = fullWidth
              }

              // Set Canvas height and top position
              if (fullHeight > naturalHeight) {
                // Case where picture is less high than the container
                // We adapt top position, because there will be margins
                top = Math.round((fullHeight - naturalHeight) / 2)
                this.canvas.style.top = top + 'px'
                height = naturalHeight
              } else if (fullHeight > height) {
                // Case where canvas is less high than the container
                // We adapt top position
                top = Math.round((fullHeight - height) / 2)
                this.canvas.style.top = top + 'px'
              } else {
                // Height is bigger than the container. So we put it
                // inside the container and adapt width parameters accordingly.
                height = fullHeight
                width = Math.round(height * ratio)
                const left = Math.round((fullWidth - width) / 2)
                this.canvas.style.left = left + 'px'
              }
              this.fabricCanvas.setDimensions({ width, height })
            }
          }
          return Promise.resolve()
        })
    },

    getComparisonTaskTypeOptions () {
      const taskTypeIds = Object.keys(
        this.currentEntity.preview_files
      ).filter(
        taskTypeId => {
          return !!this.currentEntity.preview_files[taskTypeId]
        }
      )
      const taskTypeOptions = taskTypeIds.map((taskTypeId) => {
        return {
          label: this.taskTypeMap.get(taskTypeId).name,
          value: this.taskTypeMap.get(taskTypeId).id
        }
      }).sort((a, b) => -a.label.localeCompare(b.label))
      return taskTypeOptions
    },

    isComparisonTaskTypeAvailable () {
      return this.taskTypeOptions.findIndex(
        taskTypeOption => {
          return taskTypeOption.value === this.savedTaskTypeToCompare
        }
      ) !== -1
    },

    rebuildComparisonOptions () {
      this.comparisonEntityMissing = false
      if (this.entityList.length > 0) {
        this.taskTypeOptions = this.getComparisonTaskTypeOptions()
        if (this.taskTypeOptions.length > 0) {
          if (this.isComparisonTaskTypeAvailable()) {
            this.taskTypeToCompare = this.savedTaskTypeToCompare
          } else {
            // If we couldn't find the current task type,
            // then fallback to the first one in the list.
            this.taskTypeToCompare = this.taskTypeOptions[0].value
            this.comparisonEntityMissing = true
          }
        }
        this.rebuildRevisionOptions()
      } else {
        this.taskTypeOptions = []
        this.revisionOptions = []
      }
    },

    rebuildRevisionOptions () {
      if (this.currentEntity &&
          this.currentEntity.preview_files[this.taskTypeToCompare]) {
        const revisions = this.currentEntity
          .preview_files[this.taskTypeToCompare]
          .map(p => p.revision)
        this.revisionOptions = [{
          label: 'Last',
          value: null
        }].concat(
          revisions
            .sort((a, b) => b - a)
            .map(revision => {
              return {
                label: `v${revision}`,
                value: `${revision}`
              }
            })
        )
        if (this.revisionOptions.length > 0) {
          this.revisionToCompare = this.revisionOptions[0].value
        }
      } else {
        this.revisionOptions = []
      }
    },

    rebuildEntityListToCompare () {
      if (this.taskTypeToCompare) {
        this.entityListToCompare = this.entityList
          .map(entity => {
            if (!entity.preview_files || entity.preview_files === {}) {
              return ({
                preview_file_id: '',
                preview_file_extension: 'none'
              })
            }
            let previewFiles = entity.preview_files[this.taskTypeToCompare]
            let key = this.taskTypeToCompare
            if (!previewFiles) {
              key = Object.keys(entity.preview_files)[0]
              previewFiles = entity.preview_files[key]
            }
            let preview = previewFiles.find(
              p => `${p.revision}` === this.revisionToCompare
            )
            if (!preview) {
              preview = entity.preview_files[key][0]
            }
            return ({
              preview_file_id: preview.id,
              preview_file_extension: 'mp4'
            })
          })
      } else {
        this.buildEntityListToCompare = []
      }
    },

    resetComparison () {
      this.rebuildRevisionOptions()
      this.$nextTick(() => {
        this.rawPlayerComparison.loadEntity(this.playingEntityIndex)
        this.$nextTick(() => {
          setTimeout(() => {
            this.syncComparisonPlayer()
          }, 100)
          if (this.isPlaying) this.play()
        })
      })
    },

    clearCanvas () {
      if (this.fabricCanvas) {
        this.fabricCanvas.clear()
      }
    },

    onAnnotateClicked () {
      this.showCanvas()
      if (this.isDrawing) {
        this.fabricCanvas.isDrawingMode = false
        this.isDrawing = false
      } else {
        this.isTyping = false
        if (this.fabricCanvas) {
          this.fabricCanvas.isDrawingMode = true
        }
        this.isDrawing = true
      }
    },

    onTypeClicked () {
      const clickarea = this.canvas.getElementsByClassName('upper-canvas')[0]
      this.showCanvas()
      if (this.isTyping) {
        this.isTyping = false
        clickarea.removeEventListener('dblclick', this.addText)
      } else {
        this.fabricCanvas.isDrawingMode = false
        this.isDrawing = false
        this.isTyping = true
        clickarea.addEventListener('dblclick', this.addText)
      }
    },

    showCanvas () {
      if (this.canvas) this.canvas.style.display = 'block'
    },

    hideCanvas () {
      if (this.canvas) this.canvas.style.display = 'none'
    },

    loadAnnotation (annotation) {
      if (!annotation) return
      this.pause()
      const currentTime = annotation ? annotation.time || 0 : 0
      if (this.rawPlayer || this.picturePlayer) {
        if (this.rawPlayer) {
          const frameNumber = currentTime / this.frameDuration
          this.rawPlayer.setCurrentFrame(frameNumber)
          this.syncComparisonPlayer()
          this.currentTimeRaw = currentTime
          this.updateProgressBar()
        }
        this.clearCanvas()
        this.loadSingleAnnotation(annotation)
      }
    },

    saveAnnotations () {
      let currentTime = roundToFrame(this.currentTimeRaw, this.fps) || 0
      if (currentTime < 0) currentTime = 0
      if (this.isCurrentPreviewPicture) currentTime = 0
      if (!this.annotations) return

      // Get annotations currently stored
      const annotation = this.getAnnotation(currentTime)
      // Get annotation set on the canvas
      const annotations = this.getNewAnnotations(currentTime, annotation)
      // Retrieved current entity.
      const entity = this.entityList[this.playingEntityIndex]
      if (!entity) return

      // Build a preview object to handle update
      let preview = {
        id: entity.preview_file_id,
        task_id: entity.preview_file_task_id,
        annotations: entity.preview_file_annotations || []
      }
      // If we are working on a subpreview build the preview object from it.
      if (this.currentPreviewIndex > 0) {
        const index = this.currentPreviewIndex - 1
        const previewFile = this.currentEntity.preview_file_previews[index]
        preview = {
          id: previewFile.id,
          task_id: entity.preview_file_task_id,
          annotations: previewFile.annotations || []
        }
      }
      if (!this.isCurrentUserArtist) { // Artists are not allowed to draw
        // Emit an event for remote and store update
        if (!this.notSaved) {
          this.startAnnotationSaving(preview, annotations)
        } else {
          this.$options.changesToSave = { preview, annotations }
        }

        // Update information locally
        entity.preview_file_annotations = annotations
        Object.keys(entity.preview_files).forEach(taskTypeId => {
          let revPreview = null
          entity.preview_files[taskTypeId].forEach(p => {
            if (p.id === preview.id) revPreview = p
            if (!revPreview && p.previews) {
              p.previews.forEach(subPreview => {
                if (subPreview.id === preview.id) revPreview = p
              })
            }
          })
          if (revPreview) revPreview.annotations = annotations
        })
      }
    },

    onDeleteClicked () {
      this.deleteSelection()
    },

    getAnnotation (time) {
      if (!this.annotations) {
        this.annotations = this.currentEntity.preview_file_annotations
      }
      time = roundToFrame(time, this.fps)

      if (this.annotations && this.annotations.find) {
        let annotation = this.annotations.find(
          (annotation) => annotation.time === time
        )
        if (!annotation) {
          annotation = this.annotations.find(
            (annotation) => annotation.time > time - 0.02 && annotation.time <
            time + 0.02
          )
        }
        if (!annotation &&
          this.isCurrentPreviewPicture &&
          this.annotations.length > 0
        ) {
          annotation = this.annotations[0]
          annotation.time = 0
        }
        return annotation
      } else {
        this.annotations = []
        return null
      }
    },

    toggleDlButtons () {
      this.isDlButtonsHidden = !this.isDlButtonsHidden
    },

    onBuildClicked () {
      this.runBuild(false)
    },

    onBuildFullClicked () {
      this.runBuild(true)
    },

    runBuild (full = false) {
      if (
        this.isCurrentUserManager &&
        !this.isJobRunning &&
        !this.isBuildLaunched
      ) {
        this.isBuildLaunched = true
        this.runPlaylistBuild({ playlist: this.playlist, full })
          .then(() => {
            this.isBuildLaunched = false
          })
          .catch(console.error)
      }
    },

    onRemoveBuildJob (job) {
      job.playlist_id = this.playlist.id
      this.removeBuildJob(job)
    },

    onMetadataLoaded (event) {
      this.$nextTick(() => {
        this.resetCanvasSize()
      })
    },

    showTaskTypeModal () {
      this.modals.taskType = true
    },

    hideTaskTypeModal () {
      this.modals.taskType = false
    },

    confirmChangeTaskType (taskTypeId) {
      this.$emit('task-type-changed', taskTypeId)
      this.modals.taskType = false
    },

    clearPlayer () {
      if (this.rawPlayer) this.rawPlayer.clear()
      if (this.isComparing) {
        this.$refs['raw-player-comparison'].clear()
      }
      this.maxDurationRaw = 0
      this.maxDuration = '00:00.000'
    },

    onPreviousPreviewClicked () {
      const index = this.currentPreviewIndex - 1
      this.currentPreviewIndex =
        index < 0 ? this.currentEntityPreviewLength - 1 : index
      this.updatePlayingStatus()
    },

    onNextPreviewClicked () {
      const index = this.currentPreviewIndex + 1
      this.currentPreviewIndex =
        index > this.currentEntityPreviewLength - 1 ? 0 : index
      this.updatePlayingStatus()
    },

    onPreviousComparisonPictureClicked () {
      const index = this.currentComparisonPreviewIndex - 1
      this.currentComparisonPreviewIndex =
        index < 0 ? this.currentComparisonPreviewLength - 1 : index
      this.updatePlayingStatus()
    },

    onNextComparisonPictureClicked () {
      const index = this.currentComparisonPreviewIndex + 1
      this.currentComparisonPreviewIndex =
        index > this.currentComparisonPreviewLength - 1 ? 0 : index
      this.updatePlayingStatus()
    },

    resetPictureCanvas () {
      this.annotations = this.currentPreview.annotations || []
      return this.resetCanvas()
        .then(() => {
          this.showCanvas()
          if (this.isCurrentPreviewPicture) {
            if (!this.isPlaying) this.loadAnnotation(this.getAnnotation(0))
          }
        })
    },

    // Scrubbing

    onCanvasMouseMoved (event) {
      if (this.isCurrentPreviewMovie && this.$options.scrubbing) {
        const x = event.e.clientX
        if (x - this.$options.scrubStartX < 0) {
          this.goPreviousFrame()
        } else {
          this.goNextFrame()
        }
        this.$options.scrubStartX = x
      }
    },

    onCanvasClicked (event) {
      if (event.button > 1 && this.isCurrentPreviewMovie) {
        this.$options.scrubbing = true
        this.$options.scrubStartX = event.e.clientX
        this.$options.scrubStartTime = Number(this.currentTimeRaw)
      }
      return false
    },

    onCanvasReleased (event) {
      if (this.isCurrentPreviewMovie && this.$options.scrubbing) {
        this.$options.scrubbing = false
      }
      return false
    },

    onTimeCodeClicked (
      { versionRevision, minutes, seconds, milliseconds, frame }
    ) {
      const previews = this.currentEntity.preview_files[this.task.task_type_id]
      const previewFile = previews.find(
        p => p.revision === parseInt(versionRevision)
      )
      this.onPreviewChanged(this.currentEntity, previewFile)
      const time = parseInt(minutes) * 60 + parseInt(seconds) + parseInt(milliseconds) / 1000
      setTimeout(() => {
        const frameNumber = time / this.frameDuration
        this.rawPlayer.setCurrentFrame(frameNumber)
        this.onFrameUpdate(frameNumber)
        this.syncComparisonPlayer()
      }, 20)
    },

    onTaskTypeToCompareChanged () {
      this.saveUserComparisonChoice()
      this.rebuildEntityListToCompare()
      this.updatePlayingStatus()
    },

    onRevisionToCompareChanged () {
      if (this.isComparing) {
        this.rebuildEntityListToCompare()
        this.updatePlayingStatus()
        this.$nextTick(() => {
          this.pause()
          this.rawPlayerComparison.loadEntity(this.playingEntityIndex)
          this.rawPlayerComparison.setCurrentTimeRaw(this.currentTimeRaw)
        })
      }
    },

    saveUserComparisonChoice () {
      this.savedTaskTypeToCompare = this.taskTypeToCompare
      this.sendUpdatePlayingStatus()
    },

    joinRoom () {
      if (!this.playlist.id) {
        return
      }

      this.$socket.emit('preview-room:join', {
        user_id: this.user.id, playlist_id: this.playlist.id
      })
    },

    leaveRoom () {
      if (!this.playlist.id) {
        return
      }

      this.$socket.emit('preview-room:leave', {
        user_id: this.user.id, playlist_id: this.playlist.id
      })
    },

    sendUpdatePlayingStatus () {
      if (this.isCurrentPreviewMovie) {
        // we need to wait that the video player finished updating before
        // sending the event on the websocket
        this.onNextTimeUpdateActions.push(this.updatePlayingStatus)
      } else {
        this.updatePlayingStatus()
      }
    },

    updatePlayingStatus () {
      if (!this.playlist.id) {
        return
      }
      if (!this.joinedRoom) {
        return
      }

      this.$socket.emit('preview-room:update-playing-status', {
        playlist_id: this.playlist.id,
        is_playing: this.isPlaying,
        current_entity_index: this.playingEntityIndex,
        current_frame_number: this.currentFrameMovieOrPicture,
        is_repeating: this.isRepeating,
        comparing: {
          enable: this.isComparing,
          task_type: this.taskTypeToCompare,
          revision: this.revisionToCompare,
          mode: this.comparisonMode,
          comparison_preview_index: this.currentComparisonPreviewIndex
        }
      })
    }
  },

  watch: {
    isCommentsHidden () {
      if (!this.isCommentsHidden) this.$refs['task-info'].loadTaskData()
      if (this.isSound) {
        this.soundPlayer.redraw()
      }
    },

    currentPreviewIndex () {
      this.endAnnotationSaving()
      this.resetUndoStacks()
      this.$nextTick(() => {
        if (this.isCurrentPreviewPicture) {
          this.resetPictureCanvas()
        } else {
          this.resetCanvas()
        }
      })
    },

    playingEntityIndex () {
      this.endAnnotationSaving()
      this.updateTaskPanel()
      this.resetUndoStacks()
      this.currentPreviewIndex = 0
      this.currentComparisonPreviewIndex = 0
      if (this.currentEntity) {
        this.annotations = this.currentEntity.preview_file_annotations || []
      }
      this.$nextTick(() => {
        if (this.isComparing) {
          this.rebuildComparisonOptions()
          this.rebuildRevisionOptions()
        }
        this.$nextTick(() => {
          if (this.isCurrentPreviewPicture) {
            this.resetPictureCanvas()
          } else {
            this.resetCanvas()
          }
        })
      })
    },

    isComparing () {
      if (this.isComparing) {
        this.pause()
        this.resetComparison()
        this.rebuildEntityListToCompare()
      }
      this.$nextTick()
        .then(() => {
          this.resetPictureCanvas()
          this.resetCanvas()
          this.reloadAnnotations()
        })
    },

    taskTypeToCompare () {
      if (this.isComparing) {
        this.resetComparison()
      }
    },

    revisionToCompare () {
    },

    entities () {
      this.currentPreviewIndex = 0
      this.currentComparisonPreviewuIndex = 0
      this.entityList = Object.values(this.entities)
      this.entityList.forEach((entity, i) => {
        this.framesPerImage[i] = entity.preview_nb_frames ||
          DEFAULT_NB_FRAMES_PICTURE
      })
      this.playingEntityIndex = 0
      this.pause()
      if (this.rawPlayer) this.rawPlayer.setCurrentFrame(1)
      this.currentTimeRaw = 0
      this.updateProgressBar()
      this.updateTaskPanel()
      this.rebuildComparisonOptions()
      this.clearCanvas()
      this.annotations = []
      if (this.entityList.length === 0) {
        this.clearPlayer()
      }
      this.resetHeight()
      this.resetCanvas()
        .then(() => {
          if (this.isCurrentPreview) {
            this.annotations = this.currentEntity.preview_file_annotations
            this.loadAnnotation(this.getAnnotation(0))
          }
        })
    },

    playlist () {
      this.endAnnotationSaving()
      this.forClient = Boolean(this.playlist.for_client).toString()
      this.$nextTick(() => {
        this.updateProgressBar()
        this.clearCanvas()
      })
      if (this.playlist.id) {
        this.$socket.emit('preview-room:open-playlist', {
          playlist_id: this.playlist.id
        })
      }
    },

    isAddingEntity () {
      this.$nextTick(() => {
        this.updateProgressBar()
      })
    },

    isComparisonOverlay () {
      this.$nextTick(() => {
        this.resetCanvas()
          .then(this.reloadCurrentAnnotation)
      })
    }
  },

  socket: {
    events: {
      'preview-file:annotation-update' (eventData) {
        if (
          !this.tempMode &&
          this.previewFileMap.get(eventData.preview_file_id)
        ) {
          this.refreshPreview({
            previewId: eventData.preview_file_id,
            taskId: this.currentPreview.task_id
          }).then(preview => {
            if (
              !this.notSaved &&
              this.currentPreview.id === eventData.preview_file_id &&
              !this.isWriting(eventData.updated_at)
            ) {
              const isAnnotationSizeChanged =
                this.annotations.length !== preview.annotations.length
              this.annotations = preview.annotations
              if (isAnnotationSizeChanged) this.reloadAnnotations()
              this.reloadCurrentAnnotation()
            }
            this.$emit('annotations-refreshed', preview)
          })
        }
      },

      'preview-room:room-people-updated' (eventData) {
        // someone joined the room
        this.room.people = eventData.people

        if (!this.joinedRoom) {
          return
        }
        if (this.room.newComer) {
          this.room.newComer = false
          return
        }

        this.$socket.emit('preview-room:sync-newcomer', {
          playlist_id: this.playlist.id,
          is_playing: this.isPlaying,
          current_entity_index: this.playingEntityIndex,
          current_frame_number: this.currentFrameMovieOrPicture
        })
      },

      'preview-room:room-updated' (eventData) {
        this.room.people = eventData.people

        if (!this.joinedRoom) {
          return
        }
        if (eventData.only_newcomer && !this.room.newComer) {
          return
        }

        if (eventData.is_playing !== this.isPlaying && !eventData.is_playing) {
          // pause if needed to prevent screen flickering
          this.pause()
        }

        if (
          this.exists(eventData.current_entity_index) &&
          eventData.current_entity_index !== this.playingEntityIndex
        ) {
          this.playEntity(eventData.current_entity_index)
        }

        if (this.exists(eventData.current_frame_number)) {
          if (
            this.isCurrentPreviewMovie &&
            eventData.current_frame_number !== parseInt(this.currentFrame)
          ) {
            const frameNumber = eventData.current_frame_number
            this.rawPlayer.setCurrentFrame(frameNumber)
            this.currentTimeRaw = this.rawPlayer.getCurrentTimeRaw()
            this.syncComparisonPlayer()
            this.updateProgressBar()
          } else if (
            this.isCurrentPreviewPicture &&
            eventData.current_frame_number !== this.framesSeenOfPicture
          ) {
            this.framesSeenOfPicture = eventData.current_frame_number
          }
        }

        if (
          this.exists(eventData.is_repeating) &&
          eventData.is_repeating !== this.isRepeating
        ) {
          this.isRepeating = eventData.is_repeating
        }

        if (
          this.exists(eventData.comparing)
        ) {
          this.isComparing = eventData.comparing.enable
          this.taskTypeToCompare = eventData.comparing.task_type
          this.revisionToCompare = eventData.comparing.revision
          this.comparisonMode = eventData.comparing.mode
          this.currentComparisonPreviewIndex =
            eventData.comparing.comparison_preview_index
        }

        if (eventData.is_playing !== this.isPlaying) {
          if (eventData.is_playing) {
            this.play()
          } else {
            this.pause()
          }
        }
      }

      // TODO (?) :
      // - handle updating the playlist order, adding/removing items
      // - sync playing speed
      // - sync number of frames per image
      // - sync annotations
      //   (maybe already done, see preview-file:annotation-update)
    }
  }
}
</script>

<style lang="scss" scoped>
.full-height {
  height: 100%;
}

.playlist-header {
  color: $white-grey;
  background: $dark-grey-light;

  .playlist-name {
    font-size: 1.5em;
    padding: 10px 0 10px 1em;
  }

  .playlist-room {
    flex: 1;
    font-size: 1.5em;
    padding: 10px;
  }

  .edit-button,
  .delete-button {
    height: 50px;
    width: 50px;
  }
}

.playlist-player {
  background: $dark-grey;
  display: flex;
  flex-direction: column;

  .playlist-button {
    margin: 0;
    background: $dark-grey-light;
    border: 0;
    border-radius: 0;
    color: $white-grey;

    &:hover {
      background: $dark-grey-lighter;
    }

    &.active {
      color: $green;
    }

    &.topbar-button {
      border: 1px solid $dark-grey-strong;
      border-radius: 10px;
      margin-right: 0.5em;
    }
  }
}

.playlisted-entities,
.playlist-footer {
  background: $dark-grey-light;
  color: $white-grey;
}

.playlisted-entities {
  border-top: 1px solid $dark-grey-strong;
  padding: 0.4em 0em 0 0.4em;
  overflow-x: auto;
  min-height: 600px;
  align-items: flex-start;
  height: 240px;
  min-height: 240px;
}

.loading-background {
  width: 100%;
  height: 100%;
  background: black;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.spinner {
  margin: auto;
}

.task-info-column {
  min-width: 450px;
  max-width: 450px;
  overflow-y: auto;
}

.icon {
  margin-top: -4px;
  height: 20px;
}

.smaller {
  height: 16px;
}

.right {
  margin-left: auto;
}

.video-player {
  display: flex;
  flex-direction: column;
  align-content: flex-end;
  height: 100%;
}

.video-wrapper {
  flex: 1;
  display: flex;
  background: black;
  align-items: center;
  justify-content: center;
  text-align: center;
  margin: auto;
  width: 100%;
}

.annotation-movie {
  margin: auto;
  width: 100%;
}

.time-indicator {
  color: $light-grey;
  padding-left: 0.8em;
  margin-right: 0;
}

.video-container {
  position: relative;
}

.canvas-wrapper {
  margin: auto;
  position: absolute;
  top: 0;
  left: 0;
}

.buttons {
  height: 32px;
}

.comparison-combobox {
  margin-bottom: 0;
}

.buttons .comparison-button {
  margin-left: 1em;
}

progress::-moz-progress-bar {
  background-color: #43B581;
}

progress::-webkit-progress-value {
  background-color: #43B581;
}

progress {
  width: 100%;
  border-radius: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: $grey;
  height: 8px;
  display: block;
}

.progress span#progress-bar {
  width: 100%;
  border-radius: 0;
  margin: 0;
  padding: 0;
  background-color: #43B581;
}

.mr1 {
  margin-right: 1em;
}

.mr0 {
  margin-right: 0;
}

.playlist-header,
.video-progress {
  transition: opacity 0.5s ease
}

.comparison-list,
.comparison-list p,
.comparison-list select {
  font-size: 0.8em;
}
.comparison-list select {
  height: 2.2em;
}
.comparison-missing {
  padding: 6px 10px;
  border: 1px solid $dark-grey;
  border-radius: 5px;
  background-color: $dark-grey-light;
  font-weight: bold;
  width: max-content;
}

.dl-button {
  background: $dark-grey;
  border: 1px solid $dark-grey;
  color: $white;
  display: inline-block;
  width: 190px;
  padding: 8px;
  cursor: pointer;

  &:hover {
    background: $dark-grey-light;
  }
}

.build-options {
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
  background: $dark-grey;
  border: 1px solid $dark-grey-light;
  position: absolute;
  width: 190px;
  left: -120px;
  top: -280px;
  height: 160px;
  z-index: 300;
}

.build-list {
  background: $dark-grey-stronger;
  border: 1px solid $dark-grey-light;
  position: absolute;
  width: 190px;
  left: -120px;
  top: -121px;
  height: 120px;
  overflow-y: auto;
  padding: 8px;
  z-index: 300;
}

.build-title {
  margin-bottom: 0.5em;
}

.delete-job-button {
  background: transparent;
  border-radius: 50%;
  color: $light-grey-light;
  cursor: pointer;
  padding: 3px;

  &:hover {
    background: $dark-grey-light;
  }
}

.build-spinner {
  width: 15px;
  max-width: 15px;
  margin-top: 5px;
  margin-right: 5px;
}

.spinner {
  margin-top: 80px;
  margin-left: 1em;
}

.annotation-tools {
  display: flex;
  align-items: stretch;
  height: 100%;
}

.slide-enter-active {
  transition: all .3s ease;
}
.slide-leave-active {
  transition: all .3s ease;
}
.slide-enter, .slide-leave-to {
  transform: translateX(100%);
}

.for-client {
  background: $dark-purple-strong;
  border: 2px solid $dark-purple-strong;
  color: $white;
  padding: 0.3em;
  margin-left: 1em;
  margin-right: 0;
  border-radius: 5px;
}

#playlist-annotation-canvas {
  margin: auto;
}

.playlisted-wrapper {
  margin-right: 0;
}

.picture-preview-wrapper {
  display: flex;
  height: inherit;
  justify-content: center;
  align-items: center;
  flex: 1;
}

.picture-preview-comparison-wrapper {
  display: flex;
  height: inherit;
  justify-content: center;
  align-items: center;
  flex: 1;
}

.picture-preview {
  max-height: 100%;
  max-width: 100%;
  color: var(--text);
}

.raw-player {
  margin: auto;
}

.disabled {
  color: $grey-strong;
}

.loading-wrapper {
  width: 100%;
}

.playlist-player a.playlist-button {
  padding-top: 3px;
  svg {
    width: 18px;
  }
}

.comparison-buttons {
  position: relative;
}

.comparison-combos {
  position: absolute;
  top: 33px;
  z-index: 50;
}

.comparison-index {
  min-width: 30px;
  margin: 0;
}

.disabled {
  color: $grey;
}

@media only screen and (min-width: 1600px) {
  .comparison-combos {
    top: -1px;
    left: 33px;
  }
}

.frame-per-image-input {
  padding: 2px;
  margin-left: 3px;
  background-color: $dark-grey-2;
  border: 1px solid $dark-grey-stronger;
  color: white;
  width: 3rem;
}

.person-avatar {
  display: inline-flex;
  margin-right: 4px;
}
</style>
